package com.coderknock.homework.ioc;

import com.coderknock.homework.ioc.annotation.Autowired;
import com.coderknock.homework.ioc.annotation.Controller;
import com.coderknock.homework.ioc.annotation.Service;
import com.coderknock.homework.ioc.exception.NotFindBeanException;
import com.coderknock.homework.ioc.exception.RepeatNameException;
import com.coderknock.homework.ioc.proxy.LogProxy;
import jodd.bean.BeanUtil;
import jodd.io.FileUtil;
import jodd.io.findfile.ClassScanner;
import jodd.util.ClassLoaderUtil;
import jodd.util.StringUtil;

import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;

/**
 * <p>IoC 容器</p>
 *
 * @author 三产
 * @version 1.0
 * @date 2017-06-18
 * @QQGroup 213732117
 * @website http://www.coderknock.com
 * @copyright Copyright 2017 拿客 coderknock.com  All rights reserved.
 * @since JDK 1.8
 */
public class IoCContainer {
    private static LogProxy proxy = new LogProxy();
    // 存储 设置了 name 的 Bean
    private static final Map<String, Object> nameBeanMapping = new ConcurrentHashMap<>();
    private static final Map<Class, Object> classBeanMapping = new ConcurrentHashMap<>();
    private static IoCContainer ioCContainer = new IoCContainer();

    public static IoCContainer getSingleton() {
        return ioCContainer;
    }


    public IoCContainer() {
        //注册 把所有 class 找出 Spring 实际是 BeanDefinition
        String scanPackage = Configer.props.getValue("scanPackage");
        doScanAndCreateBean(scanPackage);
        //注入
        doAutowired();
        System.out.println("Ioc 初始化成功");
    }

    private static void doAutowired() {
        BiConsumer biConsumer = (key, object) -> {
            Field[] fields = object.getClass().getSuperclass().getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    Autowired autowired = field.getAnnotation(Autowired.class);
                    String id = autowired.value().trim();
                    if (StringUtil.isEmpty(id)) {
                        BeanUtil.declared.setProperty(object, field.getName(), classBeanMapping.get(field.getType()));
                    } else {
                        BeanUtil.declared.setProperty(object, field.getName(), nameBeanMapping.get(id));
                    }
                } else {
                    continue;
                }
            }
        };
        nameBeanMapping.forEach(biConsumer);
        classBeanMapping.forEach(biConsumer);
    }


    private void doScanAndCreateBean(String scanPackage) {
        String scanPackageSrc = StringUtil.isEmpty(scanPackage) ? "/" : "/" + scanPackage.replaceAll("\\.", "/");
        URL url = ClassLoaderUtil.getResourceUrl(scanPackageSrc, ClassLoaderUtil.getSystemClassLoader());
        File containerFile = FileUtil.toContainerFile(url);
        ClassScanner classScanner = new IocClassScanner();
        // 不包括 资源文件
        classScanner.setIncludeResources(false);
        //设置排除的包（不对ioc框架包进行处理）
        classScanner.setExcludedEntries("com.coderknock.homework.ioc.*");
        classScanner.scan(containerFile.getParentFile().listFiles());
    }

    public <T> T getBean(String name, Class<T> clazz) {
        if (nameBeanMapping.containsKey(name)) {
            return (T) nameBeanMapping.get(name);
        }
        if (classBeanMapping.containsKey(clazz)) {
            return (T) classBeanMapping.get(clazz);
        }
        throw new NotFindBeanException(name, clazz);
    }

    public <T> T getBean(Class<T> clazz) {
        if (classBeanMapping.containsKey(clazz)) {
            return (T) classBeanMapping.get(clazz);
        }
        throw new NotFindBeanException(clazz);
    }

    public <T> T getBean(String name) {
        if (nameBeanMapping.containsKey(name)) {
            return (T) nameBeanMapping.get(name);
        }
        throw new NotFindBeanException(name);
    }

    private class IocClassScanner extends ClassScanner {

        @Override
        protected void onEntry(EntryData entryData) throws Exception {
            // 这里 spring 中 会判断选择代理模式
            Class<?> clazz = ClassLoaderUtil.loadClass(entryData.getName());
            //有注解 Service Controller 的需要初始化，没有则不需要
            if (clazz.isAnnotationPresent(Controller.class)) {
                Object object = proxy.getProxy(clazz);
                classBeanMapping.put(clazz, object);
            } else if (clazz.isAnnotationPresent(Service.class)) {
                Object object = proxy.getProxy(clazz);
                Service annotation = clazz.getAnnotation(Service.class);
                String targetClassName = annotation.value().trim();
                if (StringUtil.isEmpty(targetClassName)) {
                    //使用默认规则
                    // 1. 类名首字母小写
                    // 2. 如果实现接口，根据接口类型做ID
                    for (Class<?> aClass : clazz.getInterfaces()) {
                        classBeanMapping.put(aClass, object);
                    }
                }
                if (nameBeanMapping.containsKey(targetClassName)) {
                    throw new RepeatNameException(targetClassName, clazz.getName(),
                            nameBeanMapping.get(targetClassName).getClass().getName());
                }
                nameBeanMapping.put(targetClassName, object);
                classBeanMapping.put(clazz, object);
            }
        }

        private String lowerFirstChar(String name) {
            char[] chars = name.toCharArray();
            //本来就是小写则不转换
            //首字母小写
            chars[0] += 32;
            return chars.toString();
        }
    }

}

